#include <stdio.h>
#include "../quickjs.h"

#define JS_INIT_MODULE js_init_module
#define countof(x) (sizeof(x) / sizeof((x)[0]))

// JS_GetOwnPropertyNames 读取属性名  JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY
// 得到名称和长度
// JS_GetProperty  读取属性
static JSValue foreach_obj(JSContext *js_ctx, JSValueConst obj)
{
    // JS_GetOwnPropertyNames
    uint32_t len, i;
    JSPropertyEnum *tab;
    if (JS_GetOwnPropertyNames(js_ctx, &tab, &len, obj,
                               JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0)
    {
        return JS_ThrowTypeError(js_ctx, "JS_GetOwnPropertyNames fail");
    }
    printf("foreach_obj: GetOwnPropertyNames ok, len=%u\n", len);

    // 遍历
    for (i = 0; i < len; i++)
    {
        // 读取属性
        JSValue val = JS_GetProperty(js_ctx, obj, tab[i].atom);
        if (JS_IsException(val))
        {
            printf("foreach_object: JS_GetProperty is exception, idx=%u\n", i);
            return JS_UNDEFINED;
        }

        // 尝试转换为字符串
        const char *val_str = JS_ToCString(js_ctx, val);
        if (val_str == NULL)
        {
            printf("foreach_object: JS_ToString fail, idx=%u\n", i);
            return JS_UNDEFINED;
        }

        // 属性名称 JS_AtomToCString ?
        const char *key_str = JS_AtomToCString(js_ctx, tab[i].atom);
        if (key_str == NULL)
        {
            printf("foreach_object: JS_AtomToCString fail, idx=%u\n", i);
            return JS_UNDEFINED;
        }

        // 检查属性的值类型
        int32_t tag = JS_VALUE_GET_TAG(val);
        printf("foreach_object: %s = %s, is_object=%d, tag=%d\n", key_str, val_str, JS_IsObject(val), tag);
        // 是否对象和函数
        if (JS_IsObject(val) && (!JS_IsFunction(js_ctx, val)))
        {
            foreach_obj(js_ctx, val);
        }

        JS_FreeCString(js_ctx, val_str);
        JS_FreeCString(js_ctx, key_str);
        JS_FreeValue(js_ctx, val);
    }

    for (i = 0; i < len; i++)
    {
        JS_FreeAtom(js_ctx, tab[i].atom);
    }
    js_free(js_ctx, tab);

    return JS_UNDEFINED;
}
// 接受一个js的对象
// 遍历
static JSValue js_object_to_c(
    JSContext *js_ctx,
    JSValueConst this_val,
    int argc, JSValueConst *argv)
{
    return foreach_obj(js_ctx, argv[0]);
}

// 返回一个对象到js
// JS_NewObject 创建对象
// JS_SetPropertyStr 设置属性
static JSValue c_object_to_js(
    JSContext *js_ctx,
    JSValueConst this_val,
    int argc, JSValueConst *argv)
{
    JSValue obj = JS_NewObject(js_ctx);

    JS_SetPropertyStr(js_ctx, obj, "age", JS_NewUint32(js_ctx, 100));
    JS_SetPropertyStr(js_ctx, obj, "name", JS_NewString(js_ctx, "winsenye"));
    return obj;
}

// 注册一个函数

// 1 函数列表
static const JSCFunctionListEntry js_test_funcs[] = {
    /* JS_CFUNC_DEF(函数入口名称，入参个数，QuickJS C 函数) */
    JS_CFUNC_DEF("js_object_to_c", 1, js_object_to_c),
    JS_CFUNC_DEF("c_object_to_js", 0, c_object_to_js),
};

// 2 函数注册
static int js_module_init(JSContext *ctx, JSModuleDef *m)
{
    return JS_SetModuleExportList(
        ctx, m,
        js_test_funcs, countof(js_test_funcs));
}

// 3 模块导出
#define JS_INIT_MODULE js_init_module
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
{
    JSModuleDef *m;
    m = JS_NewCModule(ctx, module_name, js_module_init);
    if (!m)
        return NULL;
    JS_AddModuleExportList(ctx, m, js_test_funcs, countof(js_test_funcs));
    return m;
}